package cn.cupster.flowdemo

import android.graphics.Color
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.viewpager2.widget.ViewPager2
import cn.cupster.flowdemo.adapter.Vp2FragmentStateAdapter
import cn.cupster.flowdemo.bean.Vp2PageBean
import cn.cupster.flowdemo.bean.Vp2PageBean.Companion.ACTION_AS_FLOW
import cn.cupster.flowdemo.bean.Vp2PageBean.Companion.ACTION_BASE
import cn.cupster.flowdemo.bean.Vp2PageBean.Companion.ACTION_BUFFER
import cn.cupster.flowdemo.bean.Vp2PageBean.Companion.ACTION_CATCH
import cn.cupster.flowdemo.bean.Vp2PageBean.Companion.ACTION_CATCH_2
import cn.cupster.flowdemo.bean.Vp2PageBean.Companion.ACTION_CHANNEL_FLOW
import cn.cupster.flowdemo.bean.Vp2PageBean.Companion.ACTION_COMBINE
import cn.cupster.flowdemo.bean.Vp2PageBean.Companion.ACTION_CONFLATE
import cn.cupster.flowdemo.bean.Vp2PageBean.Companion.ACTION_FLATE_MAP_CONTACT
import cn.cupster.flowdemo.bean.Vp2PageBean.Companion.ACTION_FLATE_MAP_LATEST
import cn.cupster.flowdemo.bean.Vp2PageBean.Companion.ACTION_FLATE_MAP_MERGE
import cn.cupster.flowdemo.bean.Vp2PageBean.Companion.ACTION_FLATTEN_MERGE
import cn.cupster.flowdemo.bean.Vp2PageBean.Companion.ACTION_FLOW_OF
import cn.cupster.flowdemo.bean.Vp2PageBean.Companion.ACTION_FLOW_ON
import cn.cupster.flowdemo.bean.Vp2PageBean.Companion.ACTION_FOLD
import cn.cupster.flowdemo.bean.Vp2PageBean.Companion.ACTION_REDUCE
import cn.cupster.flowdemo.bean.Vp2PageBean.Companion.ACTION_RETRY
import cn.cupster.flowdemo.bean.Vp2PageBean.Companion.ACTION_TAKE
import cn.cupster.flowdemo.bean.Vp2PageBean.Companion.ACTION_TRANSFORM
import cn.cupster.flowdemo.bean.Vp2PageBean.Companion.ACTION_ZIP
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayoutMediator
import kotlinx.coroutines.GlobalScope

class MainActivity : AppCompatActivity() {

    private lateinit var mTabLayout: TabLayout
    private lateinit var mViewPager2: ViewPager2

    private var mTitleArr = arrayListOf<CharSequence>()
    private var mPageBeanList = mutableListOf<Vp2PageBean?>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        attachTabViewPage()
        initData()
    }

    private fun attachTabViewPage() {
        mTabLayout = findViewById(R.id.tabLayout)
        mViewPager2 = findViewById(R.id.vp2)
        mViewPager2.adapter = Vp2FragmentStateAdapter(this, mPageBeanList)

    }

    private fun initData() {
        buildPageData()
        mPageBeanList.forEachIndexed { index, bean ->
            bean?.apply {
                bId = index
            }
            mTitleArr.add(bean?.pageName?:"")
        }
        //
        TabLayoutMediator(mTabLayout, mViewPager2) { tab, position ->
            tab.text = mTitleArr[position]
        }.attach()
    }

    private fun buildPageData() {
        // 0.实际案例
        mPageBeanList.add(Vp2PageBean().apply {
            pageName = "api_demo"
        })
        // 1.flow简单使用
        mPageBeanList.add(Vp2PageBean().apply {
            pageName = "flow"
            pageBgColor = Color.WHITE
            action = ACTION_BASE
            codeStr = "flow {\n" +
                      "     for (i in 1..5) {\n" +
                      "       delay(250)\n" +
                      "       emit(i)//发射数据\n" +
                      "     }\n" +
                      "}.onCompletion { //结束\n" +
                      "     mTvResult.append(\"==========\\n\")\n" +
                      "}.collect{//collect操作相当于订阅\n" +
                      "     println(it)\n" +
                      "}"
        })
        // 2.list快捷转换flow
        mPageBeanList.add(Vp2PageBean().apply {
            pageName = "flowOf()"
            pageBgColor = Color.WHITE
            action = ACTION_FLOW_OF
            codeStr = "flowOf(1, 2, 3, 4, 5)\n" +
                    "                .onEach {\n" +
                    "                    delay(250)\n" +
                    "                }.onCompletion {\n" +
                    "                    mTvResult.append(\"==========\\n\")\n" +
                    "                }.collect {//等价于订阅操作\n" +
                    "                    mTvResult.append(\"\$it\\n\")\n" +
                    "                }"
        })
        // 3.list快速转换flow
        mPageBeanList.add(Vp2PageBean().apply {
            pageName = "asFlow()"
            pageBgColor = Color.WHITE
            action = ACTION_AS_FLOW
            codeStr = "mutableListOf(1, 2, 3, 4, 5).asFlow()\n" +
                    "                .onEach {\n" +
                    "                    delay(250)\n" +
                    "                }.onCompletion {\n" +
                    "                    mTvResult.append(\"==========\\n\")\n" +
                    "                }.collect {//等价于订阅操作\n" +
                    "                    mTvResult.append(\"\$it\\n\")\n" +
                    "                }"
        })
        // 4.使用channelFlow
        mPageBeanList.add(Vp2PageBean().apply {
            pageName = "channelFlow"
            pageBgColor = Color.WHITE
            action = ACTION_CHANNEL_FLOW
            codeStr = "//channelFlow是热数据流，flow是冷数据流\n" +
                    "//channelFlow默认实现异步非阻塞，flow需自行切换线程才能实现\n" +
                    "channelFlow<Int> {\n" +
                    "                for (num in 1..5) {\n" +
                    "                    delay(250)\n" +
                    "                    send(num)//发射数据(异步非阻塞)\n" +
                    "                }\n" +
                    "            }.onCompletion {\n" +
                    "                mTvResult.append(\"==========\\n\")\n" +
                    "            }.collect {//等价于订阅操作\n" +
                    "                mTvResult.append(\"\$it\\n\")\n" +
                    "            }"
        })
        // 5.使用flow切换线程
        mPageBeanList.add(Vp2PageBean().apply {
            pageName = "flowOn"
            pageBgColor = Color.WHITE
            action = ACTION_FLOW_ON
            codeStr =
                "flow<Int> {\n" +
                        "                for (num in 1..5) {\n" +
                        "                    delay(250)\n" +
                        "                    emit(num)//发射数据\n" +
                        "                }\n" +
                        "            }\n" +
                        "//                .flowOn(Dispatchers.IO)//切换线程，影响前面代码的执行线程，不影响后面\n" +
                        "                .onCompletion {// 执行线程会被 flowOn() 改变，map()、each{}同理\n" +
                        "                val completionThread = Thread.currentThread().name\n" +
                        "                mTvResult.post {// 非主线程不能UI更新操作,不能使用 withContext() 来切换 flow 的线程\n" +
                        "                    mTvResult.append(\"\${completionThread}:===== end =====\\n\")\n" +
                        "                }\n" +
                        "            }\n" +
                        "                .flowOn(Dispatchers.IO)//切换线程，影响前面代码的执行线程\n" +
                        "                .collect {//collect的执行线程，取决于执行协程的Scope\n" +
                        "                    mTvResult.append(\"\${Thread.currentThread().name}:\$it\\n\")\n" +
                        "                }"
        })
        // 6.catch
        mPageBeanList.add(Vp2PageBean().apply {
            pageName = "catch"
            pageBgColor = Color.WHITE
            action = ACTION_CATCH
            codeStr =
                "(1..3).asFlow()\n" +
                        "                .onEach {\n" +
                        "                    delay(250)\n" +
                        "                    if (it == 2){\n" +
                        "                        throw RuntimeException()\n" +
                        "                    }\n" +
                        "                }.catch {//catch的调用位置决定异常捕获范围\n" +
                        "                    val completionThread = Thread.currentThread().name\n" +
                        "                    mTvResult.post {\n" +
                        "                        mTvResult.append(\"\${completionThread}:catch exception\\n\")\n" +
                        "                    }\n" +
                        "                }.onCompletion { cause ->\n" +
                        "                    val completionThread = Thread.currentThread().name\n" +
                        "                    if (cause != null){//catch{}位置在前，异常已经被消费，这里捕获不到，为null,执行else\n" +
                        "                        mTvResult.post {\n" +
                        "                            mTvResult.append(\"\${completionThread}:end with exception\\n\")\n" +
                        "                        }\n" +
                        "                    }else{\n" +
                        "                        mTvResult.post {\n" +
                        "                            mTvResult.append(\"\${completionThread}:end normal\\n\")\n" +
                        "                        }\n" +
                        "                    }\n" +
                        "                }.flowOn(Dispatchers.IO).collect {//订阅，flow 才会执行\n" +
                        "                    mTvResult.append(\"\${Thread.currentThread().name}:\$it\\n\")\n" +
                        "                }"
        })
        // 7.catch2
        mPageBeanList.add(Vp2PageBean().apply {
            pageName = "catch-After"
            pageBgColor = Color.WHITE
            action = ACTION_CATCH_2
            codeStr =
                "(1..4).asFlow()\n" +
                        "                .onEach {\n" +
                        "                    delay(250)\n" +
                        "                    if (it == 3){\n" +
                        "                        throw RuntimeException()\n" +
                        "                    }\n" +
                        "                }.onCompletion { cause ->\n" +
                        "                    val completionThread = Thread.currentThread().name\n" +
                        "                    if (cause != null){\n" +
                        "                        mTvResult.post {\n" +
                        "                            mTvResult.append(\"\${completionThread}:end with exception\\n\")\n" +
                        "                        }\n" +
                        "                    }else{\n" +
                        "                        mTvResult.post {\n" +
                        "                            mTvResult.append(\"\${completionThread}:end normal\\n\")\n" +
                        "                        }\n" +
                        "                    }\n" +
                        "                }.catch {//catch的调用位置决定异常捕获范围\n" +
                        "                    val completionThread = Thread.currentThread().name\n" +
                        "                    mTvResult.post {\n" +
                        "                        mTvResult.append(\"\${completionThread}:catch exception\\n\")\n" +
                        "                    }\n" +
                        "                }.flowOn(Dispatchers.IO).collect {//订阅，flow 才会执行\n" +
                        "                    mTvResult.append(\"\${Thread.currentThread().name}:\$it\\n\")\n" +
                        "                }"
        })
        // 8.retry
        mPageBeanList.add(Vp2PageBean().apply {
            pageName = "retry"
            pageBgColor = Color.WHITE
            action = ACTION_RETRY
            codeStr =
                "(1..3).asFlow()\n" +
                        "                .onEach {\n" +
                        "                    delay(250)\n" +
                        "                    if (it == 3) {\n" +
                        "                        throw RuntimeException()\n" +
                        "                    }\n" +
                        "                }\n" +
                        "                .retry(2) {//等价于下面的retryWhen\n" +
                        "                    if (it is RuntimeException) {\n" +
                        "                        return@retry true\n" +
                        "                    }\n" +
                        "                    false\n" +
                        "                }\n" +
                        "//                .retryWhen { cause, attempt -> //attempt从0开始\n" +
                        "//                    attempt < 2\n" +
                        "//                }\n" +
                        "                .catch {\n" +
                        "                    val completionThread = Thread.currentThread().name\n" +
                        "                    mTvResult.post {\n" +
                        "                        mTvResult.append(\"\${completionThread}:catch exception\\n\")\n" +
                        "                    }\n" +
                        "                }\n" +
                        "                .collect {\n" +
                        "                    mTvResult.append(\"\${Thread.currentThread().name}:\$it\\n\")\n" +
                        "                }"
        })
        // 9.exeBuffer
        mPageBeanList.add(Vp2PageBean().apply {
            pageName = "Buffer"
            pageBgColor = Color.WHITE
            action = ACTION_BUFFER
            codeStr =
                "var startMills = 0L\n" +
                        "            (1..5).asFlow()\n" +
                        "                .onEach {\n" +
                        "                    delay(250)\n" +
                        "                }\n" +
                        "                .buffer()//注释，不使用buffer并发操作，会更慢\n" +
                        "                .onStart {\n" +
                        "                    startMills = System.currentTimeMillis()\n" +
                        "                }\n" +
                        "                .onCompletion {\n" +
                        "                    mTvResult.append(\"With buffer was Done in \${System.currentTimeMillis() - startMills}ms\\n\")\n" +
                        "                }\n" +
                        "                .collect {\n" +
                        "                    mTvResult.append(\"\${Thread.currentThread().name}:\$it\\n\")\n" +
                        "                }"
        })
        // 10.背压缓存策略 conflate
        mPageBeanList.add(Vp2PageBean().apply {
            pageName = "Conflate"
            pageBgColor = Color.WHITE
            action = ACTION_CONFLATE
            codeStr =
                "var startMills = 0L\n" +
                        "                (1..5).asFlow()\n" +
                        "                    .onStart {\n" +
                        "                        startMills = System.currentTimeMillis()\n" +
                        "                    }\n" +
                        "                    .onEach {\n" +
                        "                        delay(250)\n" +
                        "                        val completionThread = Thread.currentThread().name\n" +
                        "                        mTvResult.post {\n" +
                        "                            mTvResult.append(\"\${completionThread}: emit: \$it\\n\")\n" +
                        "                        }\n" +
                        "                    }\n" +
                        "                    //Flow的异步缓存池满了，会丢弃待入池数据，最后一条数据将强制放入缓存池中。\n" +
                        "                    .conflate()//use conflate\n" +
                        "                    .onCompletion {\n" +
                        "                        mTvResult.append(\"use conflate was Done in \${System.currentTimeMillis() - startMills}ms\\n\")\n" +
                        "                    }\n" +
                        "                    .collect {\n" +
                        "                        delay(100)\n" +
                        "                        mTvResult.append(\"conflate \${Thread.currentThread().name}:\$it\\n\")\n" +
                        "                    }"
        })
        // 11.flateMapMerge
        mPageBeanList.add(Vp2PageBean().apply {
            pageName = "flateMapMerge"
            pageBgColor = Color.WHITE
            action = ACTION_FLATE_MAP_MERGE
            codeStr =
                "(1..50).asFlow().flatMapMerge {\n" +
                        "                flow {\n" +
                        "                    emit(it)\n" +
                        "                }.flowOn(Dispatchers.IO)\n" +
                        "            }.collect {\n" +
                        "                mTvResult.append(\"flatMapMerge \${Thread.currentThread().name}:\$it\\n\")\n" +
                        "            }"
        })
        // 12.flateMapLatest
        mPageBeanList.add(Vp2PageBean().apply {
            pageName = "flateMapLatest"
            pageBgColor = Color.WHITE
            action = ACTION_FLATE_MAP_LATEST
            codeStr =
                " (1..5).asFlow().flatMapLatest {\n" +
                        "                flow {\n" +
                        "                    //当发射了新值之后，上个 flow 就会被取消\n" +
                        "                    emit(it)\n" +
                        "                    mTvResult.append(\"emit-1:\$it\\n\")\n" +
                        "                    delay(300)\n" +
                        "                    mTvResult.append(\"emit-2:\$it\\n\")\n" +
                        "                }\n" +
                        "            }.collect {\n" +
                        "                mTvResult.append(\"flatMapLatest \${Thread.currentThread().name}:\$it\\n\")\n" +
                        "            }"
        })
        // 13.flateMapContact
        mPageBeanList.add(Vp2PageBean().apply {
            pageName = "flateMapContact"
            pageBgColor = Color.WHITE
            action = ACTION_FLATE_MAP_CONTACT
            codeStr =
                "(1..5).asFlow()\n" +
                        "                .flatMapConcat {//collect 函数在收集新值之前会等待 flatMapConcat 内部的 flow 完成。\n" +
                        "                flow {\n" +
                        "                    emit(it)\n" +
                        "                    mTvResult.append(\"emit---1:\$it\\n\")\n" +
                        "                    delay(300)\n" +
                        "                    mTvResult.append(\"emit===2:\$it\\n\")\n" +
                        "                }\n" +
                        "            }.collect {\n" +
                        "                    mTvResult.append(\"flatMapMerge \${Thread.currentThread().name}:\$it\\n\")\n" +
                        "            }"
        })
        // 14.flattenMerge
        mPageBeanList.add(Vp2PageBean().apply {
            pageName = "flattenMerge"
            pageBgColor = Color.WHITE
            action = ACTION_FLATTEN_MERGE
            codeStr =
                "val flowNum = (1..5).asFlow()\n" +
                        "            val flowLetter = {\"aa\";\"bb\";\"cc\";\"dd\";\"ee\"}.asFlow()\n" +
                        "            flowOf(flowNum ,flowLetter).flattenMerge()\n" +
                        "                .onEach { delay(100) }\n" +
                        "                .collect{\n" +
                        "                    mTvResult.append(\"flattenMerge \${Thread.currentThread().name}:\$it\\n\")\n" +
                        "                }"
        })
        // 15.combine
        mPageBeanList.add(Vp2PageBean().apply {
            pageName = "combine"
            pageBgColor = Color.WHITE
            action = ACTION_COMBINE
            codeStr =
                "val flowNum = (1..4).asFlow()\n" +
                        "            val flowLetter = mutableListOf(\"a\",\"b\",\"c\",\"d\",\"e\").asFlow()\n" +
                        "            //每次从 flowA 发出新的 item ，会将其与 flowB 的最新的 item 合并。\n" +
                        "            //结果不可控\n" +
                        "            flowNum.combine(flowLetter){ a, b ->\n" +
                        "                \"\$a combine \$b\"\n" +
                        "            }.collect {\n" +
                        "                mTvResult.append(\"combine \${Thread.currentThread().name}:\$it\\n\")\n" +
                        "            }"
        })
        // 16.ZIP
        mPageBeanList.add(Vp2PageBean().apply {
            pageName = "zip"
            pageBgColor = Color.WHITE
            action = ACTION_ZIP
            codeStr =
                "val flowNum = (1..4).asFlow()\n" +
                        "            val flowLetter = mutableListOf(\"a\",\"b\",\"c\",\"d\",\"e\").asFlow()\n" +
                        "            //zip对A、B进行逐个合并，并发射\n" +
                        "            //若item数量不一致，最终结果个数==最少的那个\n" +
                        "            flowNum.zip(flowLetter){a ,b ->\n" +
                        "                \"\$a zip \$b\"\n" +
                        "            }.collect {\n" +
                        "                mTvResult.append(\"zip \${Thread.currentThread().name}:\$it\\n\")\n" +
                        "            }"
        })
        // 17.fold
        mPageBeanList.add(Vp2PageBean().apply {
            pageName = "fold"
            pageBgColor = Color.WHITE
            action = ACTION_FOLD
            codeStr =
                "val sum = (1..3).asFlow()\n" +
                        "                .fold(0){ a,b ->//参数0为累加起始值\n" +
                        "                    a + b\n" +
                        "                }\n" +
                        "            val multi = (1..3).asFlow()\n" +
                        "                .fold(1){ a,b ->//参数1为累乘起始值\n" +
                        "                    a * b\n" +
                        "                }\n" +
                        "            mTvResult.append(\"fold(0) add \${Thread.currentThread().name}:\$sum\\n\")\n" +
                        "            mTvResult.append(\"fold(1) multi \${Thread.currentThread().name}:\$multi\\n\")"
        })
        // 18.reduce
        mPageBeanList.add(Vp2PageBean().apply {
            pageName = "reduce"
            pageBgColor = Color.WHITE
            action = ACTION_REDUCE
            codeStr =
                "val sum = (1..3).asFlow()\n" +
                        "                .map {\n" +
                        "                    it * it\n" +
                        "                }\n" +
                        "                .reduce{ a,b ->//逐个合并\n" +
                        "                    a + b\n" +
                        "                }\n" +
                        "            mTvResult.append(\"reduce \${Thread.currentThread().name}:\$sum\\n\")"
        })
        // 19.take
        mPageBeanList.add(Vp2PageBean().apply {
            pageName = "take"
            pageBgColor = Color.WHITE
            action = ACTION_TAKE
            codeStr =
                "(1..5).asFlow()\n" +
                        "                .take(3)//只取前面几个emit发射的值\n" +
                        "                .collect {\n" +
                        "                    mTvResult.append(\"take \${Thread.currentThread().name}:\$it\\n\")\n" +
                        "                }"
        })
        // 20.transform
        mPageBeanList.add(Vp2PageBean().apply {
            pageName = "transform"
            pageBgColor = Color.WHITE
            action = ACTION_TRANSFORM
            codeStr =
                " (1..5).asFlow()\n" +
                        "               .transform<Int,Int> {\n" +
                        "                   //transform操作可以多次调用emit\n" +
                        "                   emit(it )\n" +
                        "                   emit(it * it)\n" +
                        "               }.collect {\n" +
                        "                   mTvResult.append(\"transform \${Thread.currentThread().name}:\$it\\n\")\n" +
                        "               }"
        })
    }

}